﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SVM_Sharp.Controller;
using SVM_Sharp.Model;

namespace SVM_Sharp
{
    public partial class GerenciadorTestes : Form
    {
        private bool m_rangeTest;
        private int m_id;

        private const int LINHA_UM = 0;

        private const int COLUNA_ID = 0;
        private const int COLUNA_C = 1;
        private const int COLUNA_SCALE = 2;
        private const int COLUNA_KERNEL = 3;
        private const int COLUNA_GAMMA = 4;
        private const int COLUNA_COEF = 5;
        private const int COLUNA_DEGREE = 6;
        private const int COLUNA_PARAMETRO = 7;

        private Parametros m_parametros;
        private List<Parametros> m_listaParametros;

        public GerenciadorTestes()
        {
            InitializeComponent();

            m_rangeTest = false;
            m_id = 0;

            configurarParametros();
            configurarControles();
        }

        private void configurarControles()
        {
            radioButtonLinear.Checked = true;
            disableControlsKernelLinear(true);

            checkBoxIntervalos.Checked = false;
            disableControlsTestRange(true);

            checkBoxScale.Checked = true;

            textBoxCParam.Text = "1";
        }

        private void configurarParametros()
        {
            m_parametros = new Parametros();
            m_listaParametros = new List<Parametros>();

            m_parametros.setKernel(Defines.TIPO_KERNEL_LINEAR);
            m_parametros.setScale(true);
            m_parametros.setC(1);
            m_parametros.setPesoPositivo(1);
            m_parametros.setPesoNegativo(1);
        }

        private void textBoxCParam_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxCParam.Text;
            m_parametros.setC(double.Parse(text));
        }

        private void textBoxPesoPositivo_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxPesoPositivo.Text;
            m_parametros.setPesoPositivo(double.Parse(text));
        }

        private void textBoxPesoNegativo_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxPesoNegativo.Text;
            m_parametros.setPesoNegativo(double.Parse(text));
        }

        private void radioButtonLinear_CheckedChanged(object sender, EventArgs e)
        {
            m_parametros.setKernel(Defines.TIPO_KERNEL_LINEAR);

            disableControlsKernelLinear(false);
            disableControlsKernelRBF(true);
            disableControlsKernelSigmoide(true);
            disableControlsKernelPolinomial(true);

            if (m_rangeTest)
            {
                disableControlsTestRange(false);
            }
        }

        private void radioButtonRBF_CheckedChanged(object sender, EventArgs e)
        {
            m_parametros.setKernel(Defines.TIPO_KERNEL_RBF);

            disableControlsKernelLinear(true);
            disableControlsKernelRBF(false);
            disableControlsKernelSigmoide(true);
            disableControlsKernelPolinomial(true);

            if (m_rangeTest)
            {
                disableControlsTestRange(false);
            }
        }

        private void radioButtonSigmoide_CheckedChanged(object sender, EventArgs e)
        {
            m_parametros.setKernel(Defines.TIPO_KERNEL_SIGMOIDE);

            disableControlsKernelLinear(true);
            disableControlsKernelRBF(true);
            disableControlsKernelSigmoide(false);
            disableControlsKernelPolinomial(true);

            if (m_rangeTest)
            {
                disableControlsTestRange(false);
            }
        }

        private void radioButtonPolinomial_CheckedChanged(object sender, EventArgs e)
        {
            m_parametros.setKernel(Defines.TIPO_KERNEL_POLINOMIAL);

            disableControlsKernelLinear(true);
            disableControlsKernelRBF(true);
            disableControlsKernelSigmoide(true);
            disableControlsKernelPolinomial(false);

            if (m_rangeTest)
            {
                disableControlsTestRange(false);
            }
        }

        private void textBoxRBFGamma_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxRBFGamma.Text;
            m_parametros.setGamma(double.Parse(text));
        }

        private void textBoxSigmoideGamma_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxSigmoideGamma.Text;
            m_parametros.setGamma(double.Parse(text));
        }

        private void textBoxSigmoideCoef_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxSigmoideCoef.Text;
            m_parametros.setCoef(double.Parse(text));
        }

        private void textBoxPolinomialGamma_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxPolinomialGamma.Text;
            m_parametros.setGamma(double.Parse(text));
        }

        private void textBoxPolinomialCoef_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxPolinomialCoef.Text;
            m_parametros.setCoef(double.Parse(text));
        }

        private void textBoxPolinomialDegree_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxPolinomialDegree.Text;
            m_parametros.setDegree(int.Parse(text));
        }

        private void checkBoxIntervalos_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxIntervalos.Checked)
            {
                m_rangeTest = true;
                disableControlsTestRange(false);
            }
            else
            {
                m_rangeTest = false;
                disableControlsTestRange(true);
            }
        }

        private void textBoxCMax_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxCMax.Text;
            m_parametros.setMaxC(double.Parse(text));
        }

        private void textBoxCGrao_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxCGrao.Text;
            m_parametros.setGraoC(double.Parse(text));
        }

        private void textBoxGammaMax_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxGammaMax.Text;
            m_parametros.setMaxGamma(double.Parse(text));
        }

        private void textBoxGammaGrao_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxGammaGrao.Text;
            m_parametros.setGraoGamma(double.Parse(text));
        }

        private void textBoxCoefMax_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxCoefMax.Text;
            m_parametros.setMaxCoef(double.Parse(text));
        }

        private void textBoxCoefGrao_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxCoefGrao.Text;
            m_parametros.setGraoCoef(double.Parse(text));
        }

        private void textBoxDegreeMax_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxDegreeMax.Text;
            m_parametros.setMaxDegree(double.Parse(text));
        }

        private void textBoxDegreeGrao_TextChanged(object sender, EventArgs e)
        {
            string text = textBoxDegreeGrao.Text;
            m_parametros.setGraoDegree(int.Parse(text));
        }

        private void disableControlsKernelLinear(bool _enable)
        {
        }

        private void disableControlsKernelLinearRangeTest(bool _enable)
        {
            textBoxGammaMax.Enabled = _enable;
            textBoxGammaGrao.Enabled = _enable;
            textBoxCoefMax.Enabled = _enable;
            textBoxCoefGrao.Enabled = _enable;
            textBoxDegreeMax.Enabled = _enable;
            textBoxDegreeGrao.Enabled = _enable;
        }

        private void disableControlsKernelRBF(bool _enable)
        {
            textBoxRBFGamma.Enabled = !_enable;
        }

        private void disableControlsKernelRBFRangeTest(bool _enable)
        {
            textBoxGammaMax.Enabled = !_enable;
            textBoxGammaGrao.Enabled = !_enable;
            textBoxCoefMax.Enabled = _enable;
            textBoxCoefGrao.Enabled = _enable;
            textBoxDegreeMax.Enabled = _enable;
            textBoxDegreeGrao.Enabled = _enable;
        }

        private void disableControlsKernelSigmoide(bool _enable)
        {
            textBoxSigmoideGamma.Enabled = !_enable;
            textBoxSigmoideCoef.Enabled = !_enable;
        }

        private void disableControlsKernelSigmoideRangeTest(bool _enable)
        {
            textBoxGammaMax.Enabled = !_enable;
            textBoxGammaGrao.Enabled = !_enable;
            textBoxCoefMax.Enabled = !_enable;
            textBoxCoefGrao.Enabled = !_enable;
            textBoxDegreeMax.Enabled = _enable;
            textBoxDegreeGrao.Enabled = _enable;
        }

        private void disableControlsKernelPolinomial(bool _enable)
        {
            textBoxPolinomialGamma.Enabled = !_enable;
            textBoxPolinomialCoef.Enabled = !_enable;
            textBoxPolinomialDegree.Enabled = !_enable;
        }

        private void disableControlsKernelPolinomialRangeTest(bool _enable)
        {
            textBoxGammaMax.Enabled = !_enable;
            textBoxGammaGrao.Enabled = !_enable;
            textBoxCoefMax.Enabled = !_enable;
            textBoxCoefGrao.Enabled = !_enable;
            textBoxDegreeMax.Enabled = !_enable;
            textBoxDegreeGrao.Enabled = !_enable;
        }

        private void disableControlsTestRange(bool _enable)
        {
            textBoxCMax.Enabled = !_enable;
            textBoxCGrao.Enabled = !_enable;

            if (!_enable)
            {
                switch (m_parametros.getKernel())
                {
                    case Defines.TIPO_KERNEL_LINEAR:
                        disableControlsKernelLinearRangeTest(_enable);
                        break;

                    case Defines.TIPO_KERNEL_RBF:
                        disableControlsKernelRBFRangeTest(_enable);
                        break;

                    case Defines.TIPO_KERNEL_SIGMOIDE:
                        disableControlsKernelSigmoideRangeTest(_enable);
                        break;

                    case Defines.TIPO_KERNEL_POLINOMIAL:
                        disableControlsKernelPolinomialRangeTest(_enable);
                        break;
                }
            }
            else
            {
                textBoxGammaMax.Enabled = !_enable;
                textBoxGammaGrao.Enabled = !_enable;
                textBoxCoefMax.Enabled = !_enable;
                textBoxCoefGrao.Enabled = !_enable;
                textBoxDegreeMax.Enabled = !_enable;
                textBoxDegreeGrao.Enabled = !_enable;
            }
        }

        private void buttonExportar_Click(object sender, EventArgs e)
        {
            if (sfd_exportar1.ShowDialog() != DialogResult.OK)
                return;
            dsTestes.WriteXml(sfd_exportar1.FileName);

        }

        private void buttonImportar_Click(object sender, EventArgs e)
        {
            if (System.Windows.Forms.MessageBox.Show("Quer remover os resultados atuais?", "Importar", MessageBoxButtons.YesNo) == DialogResult.Yes)
                dsTestes.Tables[0].Clear();

            if (ofd_importar1.ShowDialog() != DialogResult.OK)
                return;

            try
            {
                dsTestes.ReadXml(ofd_importar1.FileName);
            }
            catch (System.Exception x)
            {
                System.Windows.Forms.MessageBox.Show(this, "Erro ao importar testes:\n " + x, "Erro", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
                return;
            }
        }

        private void buttonAdicionar_Click(object sender, EventArgs e)
        {
            try
            {
                m_parametros.setMultithread(checkBoxMultithread.Checked);
                m_parametros.setScale(checkBoxScale.Checked);
                if (m_rangeTest)
                {
                    validadeTextBox(textBoxCMax, textBoxCParam);
                    validadeTextBox(textBoxGammaMax, textBoxRBFGamma);
                    validadeTextBox(textBoxGammaMax, textBoxSigmoideGamma);
                    validadeTextBox(textBoxGammaMax, textBoxPolinomialGamma);
                    validadeTextBox(textBoxCoefMax, textBoxSigmoideCoef);
                    validadeTextBox(textBoxCoefMax, textBoxPolinomialCoef);
                    validadeTextBox(textBoxDegreeMax, textBoxPolinomialDegree);

                    double inc_c;
                    calcularSomadorIntervalo(m_parametros.getC(), m_parametros.getMaxC(), m_parametros.getGraoC(), out inc_c);

                    switch (m_parametros.getKernel())
                    {
                        case Defines.TIPO_KERNEL_LINEAR:
                            {
                                for (double c = m_parametros.getC(); c <= m_parametros.getMaxC(); c = c + inc_c)
                                {
                                    Parametros newParametros = new Parametros(m_parametros);
                                    newParametros.setC(c);

                                    adicionarLinha(newParametros);
                                }
                                break;
                            }

                        case Defines.TIPO_KERNEL_RBF:
                            {
                                double inc_gamma;
                                calcularSomadorIntervalo(m_parametros.getGamma(), m_parametros.getMaxGamma(), m_parametros.getGraoGamma(), out inc_gamma);

                                for (double c = m_parametros.getC(); c <= m_parametros.getMaxC(); c = c + inc_c)
                                {
                                    for (double gamma = m_parametros.getGamma(); gamma <= m_parametros.getMaxGamma(); gamma = gamma + inc_gamma)
                                    {
                                        Parametros newParametros = new Parametros(m_parametros);
                                        newParametros.setC(c);
                                        newParametros.setGamma(gamma);

                                        adicionarLinha(newParametros);
                                    }
                                }
                                break;
                            }

                        case Defines.TIPO_KERNEL_SIGMOIDE:
                            {
                                double inc_gamma;
                                calcularSomadorIntervalo(m_parametros.getGamma(), m_parametros.getMaxGamma(), m_parametros.getGraoGamma(), out inc_gamma);

                                double inc_coef;
                                calcularSomadorIntervalo(m_parametros.getCoef(), m_parametros.getMaxCoef(), m_parametros.getGraoCoef(), out inc_coef);

                                for (double c = m_parametros.getC(); c <= m_parametros.getMaxC(); c = c + inc_c)
                                {
                                    for (double gamma = m_parametros.getGamma(); gamma <= m_parametros.getMaxGamma(); gamma = gamma + inc_gamma)
                                    {
                                        for (double coef = m_parametros.getCoef(); coef <= m_parametros.getMaxCoef(); coef = coef + inc_coef)
                                        {
                                            Parametros newParametros = new Parametros(m_parametros);
                                            newParametros.setC(c);
                                            newParametros.setGamma(gamma);
                                            newParametros.setCoef(coef);

                                            adicionarLinha(newParametros);
                                        }
                                    }
                                }
                                break;
                            }

                        case Defines.TIPO_KERNEL_POLINOMIAL:
                            {
                                double inc_gamma;
                                calcularSomadorIntervalo(m_parametros.getGamma(), m_parametros.getMaxGamma(), m_parametros.getGraoGamma(), out inc_gamma);

                                double inc_coef;
                                calcularSomadorIntervalo(m_parametros.getCoef(), m_parametros.getMaxCoef(), m_parametros.getGraoCoef(), out inc_coef);

                                double dinc_deg;
                                calcularSomadorIntervalo(m_parametros.getDegree(), m_parametros.getMaxDegree(), m_parametros.getGraoDegree(), out dinc_deg);
                                int inc_deg = (int)dinc_deg;

                                for (double c = m_parametros.getC(); c <= m_parametros.getMaxC(); c = c + inc_c)
                                {
                                    for (double gamma = m_parametros.getGamma(); gamma <= m_parametros.getMaxGamma(); gamma = gamma + inc_gamma)
                                    {
                                        for (double coef = m_parametros.getCoef(); coef <= m_parametros.getMaxCoef(); coef = coef + inc_coef)
                                        {
                                            for (int degree = m_parametros.getDegree(); degree <= m_parametros.getMaxDegree(); degree = degree + inc_deg)
                                            {
                                                Parametros newParametros = new Parametros(m_parametros);
                                                newParametros.setC(c);
                                                newParametros.setGamma(gamma);
                                                newParametros.setCoef(coef);
                                                newParametros.setDegree(degree);

                                                adicionarLinha(newParametros);
                                            }
                                        }
                                    }
                                }
                                break;
                            }
                    }
                    return;
                }

                adicionarLinha(m_parametros);
                m_parametros = new Parametros(m_parametros);
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(this, ex.Message, "Erro");
            }

            labelNumeroTestes.Text = System.String.Format("Número de Testes: {0}", dsTestes.Tables[0].Rows.Count);
        }

        private void validadeTextBox(TextBox txtBoxMax, TextBox txtBoxMin)
        {
            if (txtBoxMin.Enabled && txtBoxMax.Enabled && txtBoxMax.Text.Length == 0)
                txtBoxMax.Text = txtBoxMin.Text;
        }

        private void adicionarLinha(Parametros _parametros)
        {
            System.Data.DataRow tmprow = dsTestes.Tables[0].NewRow();
            tmprow[COLUNA_ID] = ++m_id;
            tmprow[COLUNA_C] = _parametros.getC();
            tmprow[COLUNA_SCALE] = _parametros.getScale();
            tmprow[COLUNA_KERNEL] = _parametros.getKernel();
            tmprow[COLUNA_GAMMA] = _parametros.getGamma();
            tmprow[COLUNA_COEF] = _parametros.getCoef();
            tmprow[COLUNA_DEGREE] = _parametros.getDegree();

            tmprow[COLUNA_PARAMETRO] = _parametros;

            try
            {
                dsTestes.Tables[0].Rows.Add(tmprow);
                dgvTestes.FirstDisplayedScrollingRowIndex = dgvTestes.RowCount - 1;
            }
            catch (System.Exception x)
            {
                System.Diagnostics.Debug.WriteLine("Erro ao salvar testes:\n " + x.ToString());
                return;
            }
        }

        private void buttonRemover_Click(object sender, EventArgs e)
        {
            if (this.dgvTestes.SelectedRows.Count > 0)
            {
                foreach (DataGridViewRow row in dgvTestes.SelectedRows)
                {
                    if (!row.IsNewRow)
                        dgvTestes.Rows.Remove(row);
                }
            }
            labelNumeroTestes.Text = System.String.Format("Número de Testes: {0}", dsTestes.Tables[0].Rows.Count);
        }

        private void buttonExecutar_Click(object sender, EventArgs e)
        {
            foreach(System.Data.DataRow row in dsTestes.Tables[0].Rows)
            {
                Parametros parametro = (Parametros) row[COLUNA_PARAMETRO];
                m_listaParametros.Add(parametro);
            }

            CtrlTestManager.ligarModoGerenciador(true);
            CtrlTestManager.atribuirListaParametros(m_listaParametros);

            this.Dispose();
        }

        private void buttonCancelar_Click(object sender, EventArgs e)
        {
            this.Dispose();
        }

        private void calcularSomadorIntervalo(double inicio, double fim, double grao, out double out_mult)
        {
            out_mult = Math.Abs(grao);
            if (inicio > fim)
                out_mult *= -1;

            double range = Math.Abs(inicio - fim);
            double out_num;
            if (range == 0)
            {
                out_num = 1;
                if (out_mult >= 0)
                    out_mult = 1;
                else
                    out_mult = -1;
                return;
            }
            else if (out_mult == 0)
                out_num = 0;
            else
                out_num = Math.Abs(range / out_mult);

            if ((out_num == 0) || (Math.Abs((inicio + out_mult) - fim) >= Math.Abs(inicio - fim)))
                throw new Exception("Range impossível.");
        }

        private void btLimpar_Click(object sender, EventArgs e)
        {
            dsTestes.Tables[0].Rows.Clear();
            m_id = 0;
        }
    }
}